[入門]API Gatewayを利用してS3バケットのオブジェクトを一覧表示させてみた(Lambda統合)

[入門]API Gatewayを利用してS3バケットのオブジェクトを一覧表示させてみた(Lambda統合)

Clock Icon2024.09.30

こんにちは。中村です。

はじめに

API GatewayでLambda統合する構成は、柔軟性があって使いやすくありませんか?
私が初めて利用した時には、感動した思い出があります。
今回は、下記記事でS3からのレスポンスがxml形式であったことに対して、Lambdaプロキシ統合を利用することで解決してみます。

https://dev.classmethod.jp/articles/api-gateway-s3-list/

やってみる

テンプレートを作成してみる

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  AppName: 
    Description: Application Name.
    Type: String
    Default: api-gateway-s3-list-lambda

Resources:
# ==========
# S3
# ==========
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption: 
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: !Sub "api-gateway-s3-${AWS::AccountId}"
      OwnershipControls: 
        Rules:
          - ObjectOwnership: BucketOwnerEnforced
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
# ==========
# Lambda
# ==========
  LambdaExecutionRoleList:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: !Sub ${AppName}-listPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:ListBucket
                Resource: !Sub "arn:aws:s3:::${S3Bucket}"
      Path: /
      RoleName: !Sub ${AppName}-LambdaExecutionRoleList
  LambdaFunctionForList:
    Type: AWS::Lambda::Function
    Properties:
      Code: 
        ZipFile: |
          import boto3
          import json
          import os
          import time

          S3_BUCKET_NAME = os.environ["S3_BUCKET_NAME"]
          s3_client = boto3.client("s3")

          def lambda_handler(event, context):
              params = {'Bucket': S3_BUCKET_NAME}

              objects = []
              response = s3_client.list_objects_v2(**params)

              for obj in response.get('Contents', []):
                  objects.append({
                      'Key': obj['Key'],
                      'Size': obj['Size']
                  })

              body_str = json.dumps(objects)
              return {
                  'statusCode': 200,
                  'body': body_str,
                  'headers': {
                      'Content-Type': "application/json",
                      'Content-Length': str(len(body_str)),
                      'Timestamp': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
                  }
              }
      Environment: 
        Variables:
          S3_BUCKET_NAME: !Ref S3Bucket
      FunctionName: !Sub ${AppName}-lmd-s3List
      Handler: index.lambda_handler
      MemorySize: 128
      Role: !GetAtt LambdaExecutionRoleList.Arn
      Runtime: python3.12
      Timeout: 10
# ==========
# API Gateway
# ==========
  ApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      BinaryMediaTypes: 
        - 'image/*'
      EndpointConfiguration: 
        Types:
          - REGIONAL
      Name: !Ref AppName
  ApiGatewayMethodRootList:
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration: 
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub 
          - arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaArn}/invocations
          - LambdaArn: !GetAtt LambdaFunctionForList.Arn
      MethodResponses: 
        - StatusCode: 200
      ResourceId: !GetAtt ApiGatewayRestApi.RootResourceId
      RestApiId: !Ref ApiGatewayRestApi
  LambdaPermissionRootGET:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref LambdaFunctionForList
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub 
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${MyApi}/*/GET/
        - MyApi: !Ref ApiGatewayRestApi

やってみた

  1. 上記テンプレートを利用してCloudFormationでリソースを作成する
  2. 作成されたS3バケットに任意のファイルをアップロードする
  3. API Gatewayサービスから、作成したAPIの「テスト」タブを表示させる

api-gateway-s3-list-lambda-img-1

  1. 「テスト」を押下する

api-gateway-s3-list-img-3

  1. レスポンスを確認する

api-gateway-s3-list-lambda-img-2

  1. Lambdaで整形したjson形式で、2でアップロードしたファイル名が出力されていることが確認できます。

削除する際の留意点

Lambdaを実行した際にCloudWatch logsのロググループにLambdaのログが出力されています。
リソースを削除する際は、ロググループの削除も必要に応じて実施ください。

さいごに

今回は、Lambdaプロキシ統合を利用したAPI Gatewayを構築してみました。
Lambdaを利用することによって細かい処理にも対応できるようになるのが嬉しいですね。
本記事で紹介しているコードはサンプルであり、パフォーマンスや例外処理など一切考慮していないため、本番運用する際は十分にご検討ください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.